package com.stellar.pay.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.stellar.pay.config.WxPayConfig;
import com.stellar.pay.core.convert.ConvertWxResultToDTO;
import com.stellar.pay.core.entity.OrderQueryResultDTO;
import com.stellar.pay.core.entity.RefundResultDTO;
import com.stellar.pay.core.service.IPaymentNotifyHandler;
import com.stellar.pay.core.service.IWxPayCommonService;
import com.stellar.pay.core.utils.WxPayUtil;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RSAPublicKeyNotificationConfig;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.model.TransactionAmount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.stream.Collectors;


/**
 * @Author Jqcc
 * @Version 1.0
 * @Data 2025/4/5
 */
@Service
@Slf4j
@AllArgsConstructor
public class WxPayCommonServiceImpl implements IWxPayCommonService {

    private final WxPayConfig wxPayConfig;

    private final WxPayUtil wxPayUtil;

    private final IPaymentNotifyHandler paymentNotifyHandler;

    private final JsapiServiceExtension jsapiServiceExtension;

    private final RefundService refundService;

    @Override
    public void handlePaymentNotifyByDIY(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 读取必要参数
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String signature = request.getHeader("Wechatpay-Signature");

            // 读取请求体
            String body = readRequestBody(request);
            if (body == null) {
                writeResponse(response, "FAIL", "请求体为空");
                return;
            }

            log.info("WxPayJsapiServiceImpl.handlePaymentNotifyByDIY.body.微信回调请求体:{}", body);

            // 验签
            if (!wxPayUtil.signatureVerify(timestamp + "\n" + nonce + "\n" + body + "\n", signature)) {
                writeResponse(response, "FAIL", "验签失败");
                return;
            }

            // 解密处理
            String decryptData = wxPayUtil.decryptOrder(body);
            JSONObject result = JSON.parseObject(decryptData);

            log.info("WxPayJsapiServiceImpl.handlePaymentNotifyByDIY.decryptData.解密后的数据:{}", result);


            String orderNo = (String) result.get("out_trade_no");

            // 业务处理
            if ("SUCCESS".equals(result.get("trade_state"))) {
                JSONObject amountObj = result.getJSONObject("amount");
                BigDecimal amount = new BigDecimal(amountObj.getInteger("total"))
                        .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);

                paymentNotifyHandler.handlePaymentSuccess(
                        orderNo,
                        (String) result.get("transaction_id"),
                        amount,
                        (String) result.get("success_time"),
                        (String) result.get("trade_type"),
                        result
                );

                writeResponse(response, "FAIL", "验签失败");
            } else {
                paymentNotifyHandler.handlePaymentFailure(orderNo, "支付失败，状态：" + result.get("trade_state"));
            }
        } catch (Exception e) {
            log.error("支付回调处理异常", e);
            writeResponse(response, "FAIL", "系统异常");
        }
    }

    @Override
    public void handlePaymentNotifyBySDK(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 读取必要参数
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String signature = request.getHeader("Wechatpay-Signature");
            String serial = request.getHeader("Wechatpay-Serial");

            // 读取请求体
            String body = readRequestBody(request);
            if (body == null) {
                writeResponse(response, "FAIL", "请求体为空");
                return;
            }

            log.info("WxPayJsapiServiceImpl.handlePaymentNotifyBySDK.body.微信回调请求体:{}", body);

            // 构建回调通知的请求参数
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .body(body)
                    .build();

            // 回调通知配置
            RSAPublicKeyNotificationConfig notificationConfig = new RSAPublicKeyNotificationConfig.Builder()
                    .publicKeyFromPath(wxPayConfig.getWxPublicKeyPath())
                    .publicKeyId(wxPayConfig.getWxPublicKeyId())
                    .apiV3Key(wxPayConfig.getApiV3Key())
                    .build();

            // 回调通知解析器
            NotificationParser notificationParser = new NotificationParser(notificationConfig);
            Transaction transaction = null;
            try {
                transaction = notificationParser.parse(requestParam, Transaction.class);
                log.info("WxPayJsapiServiceImpl.handlePaymentNotifyBySDK.transaction.解析后的数据:{}", transaction);
            } catch (Exception e) {
                // 签名验证失败, 响应给业务方
                log.error("签名验证失败", e);
                String orderNo = transaction.getOutTradeNo();
                Transaction.TradeStateEnum tradeState = transaction.getTradeState();
                paymentNotifyHandler.handlePaymentFailure(orderNo, "签名验证失败，状态：" + tradeState);
                writeResponse(response, "FAIL", "签名验证失败");
                return;
            }

            String orderNo = transaction.getOutTradeNo();
            String transactionId = transaction.getTransactionId();
            TransactionAmount amount = transaction.getAmount();
            String successTime = transaction.getSuccessTime();
            Transaction.TradeTypeEnum tradeType = transaction.getTradeType();
            Transaction.TradeStateEnum tradeState = transaction.getTradeState();

            if (Transaction.TradeStateEnum.SUCCESS.equals(tradeState)) {
                paymentNotifyHandler.handlePaymentSuccess(orderNo,
                        transactionId,
                        BigDecimal.valueOf(amount.getTotal()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP),
                        successTime,
                        tradeType.toString(),
                        transaction);
                writeResponse(response, "SUCCESS", "支付成功");
            } else {
                paymentNotifyHandler.handlePaymentFailure(orderNo, "支付失败，状态：" + tradeState);
                writeResponse(response, "FAIL", "支付未成功");
            }

        } catch (Exception e) {
            log.error("支付回调处理异常", e);
            writeResponse(response, "FAIL", "处理异常");
        }
    }

    @Override
    public void closeOrder(String outTradeNo) {
        CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
        closeOrderRequest.setOutTradeNo(outTradeNo);
        closeOrderRequest.setMchid(wxPayConfig.getMchId());

        jsapiServiceExtension.closeOrder(closeOrderRequest);

        log.info("WxPayJsapiServiceImpl.closeOrder.关闭订单成功:{}", outTradeNo);

    }

    @Override
    public OrderQueryResultDTO getOrderByOutTradeNo(String outTradeNo) {
        try {
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setOutTradeNo(outTradeNo);
            request.setMchid(wxPayConfig.getMchId());
            Transaction transaction = jsapiServiceExtension.queryOrderByOutTradeNo(request);
            if (transaction.getTradeState() == Transaction.TradeStateEnum.CLOSED) {
                return ConvertWxResultToDTO.convertClosedOrderToDTO(transaction);
            } else {
                OrderQueryResultDTO orderQueryResultDTO = ConvertWxResultToDTO.convertToOrderQueryResult(transaction);
                log.info("WxPayJsapiServiceImpl.getOrderByOutTradeNo.查询订单成功:{}", orderQueryResultDTO);
                return orderQueryResultDTO;
            }
        } catch (Exception e) {
            log.error("WxPayJsapiServiceImpl.getOrderByOutTradeNo.查询订单异常", e);
        }
        return null;
    }

    @Override
    public OrderQueryResultDTO getOrderByTransactionId(String transactionId) {
        try {
            QueryOrderByIdRequest request = new QueryOrderByIdRequest();
            request.setMchid(wxPayConfig.getMchId());
            request.setTransactionId(transactionId);
            Transaction transaction = jsapiServiceExtension.queryOrderById(request);
            if (transaction.getTradeState() == Transaction.TradeStateEnum.CLOSED) {
                return ConvertWxResultToDTO.convertClosedOrderToDTO(transaction);
            } else {
                OrderQueryResultDTO orderQueryResultDTO = ConvertWxResultToDTO.convertToOrderQueryResult(transaction);
                log.info("WxPayJsapiServiceImpl.getOrderByTransactionId.查询订单成功:{}", orderQueryResultDTO);
                return orderQueryResultDTO;
            }
        } catch (Exception e) {
            log.error("WxPayJsapiServiceImpl.getOrderByTransactionId.查询订单异常", e);
        }
        return null;
    }

    @Override
    public RefundResultDTO refund(String outTradeNo, BigDecimal totalFee, BigDecimal reFee, String outRefundNo) {
        try {
            // 退款金额
            AmountReq amountReq = new AmountReq();
            amountReq.setTotal(totalFee.multiply(new BigDecimal("100")).longValue()); // 原订单交易金额
            amountReq.setRefund(reFee.multiply(new BigDecimal("100")).longValue()); // 退款金额,不能大于原支付金额
            amountReq.setCurrency("CNY"); // 货币类型 CNY 人民币,境内商户号仅支持人民币

            // 退款请求参数
            CreateRequest request = new CreateRequest();
            request.setOutTradeNo(outTradeNo); // 商户订单号
            request.setOutRefundNo(outRefundNo); // 商户退款单号
            request.setAmount(amountReq); // 退款金额

            log.info("WxPayJsapiServiceImpl.refund.退款请求参数:{}", request);

            Refund refund = refundService.create(request);

            RefundResultDTO refundResultDTO = ConvertWxResultToDTO.convertToRefundResult(refund);

            log.info("WxPayJsapiServiceImpl.refund.退款结果:{}", refundResultDTO);

            return refundResultDTO;
        } catch (ServiceException e) {
            String errorMsg;
            if (e.getHttpStatusCode() == 400) {
                if ("INVALID_REQUEST".equals(e.getErrorCode())) {
                    if (e.getErrorMessage().contains("订单已全额退款")) {
                        errorMsg = "该订单已全额退款，无法重复退款";
                    } else if (e.getErrorMessage().contains("申请退款金额超过订单可退金额")) {
                        errorMsg = "退款金额超过订单可退金额";
                    } else {
                        errorMsg = "退款请求参数错误: " + e.getErrorMessage();
                    }
                } else {
                    errorMsg = "微信支付服务异常: " + e.getErrorCode() + " - " + e.getErrorMessage();
                }
            } else {
                errorMsg = "微信支付服务异常: " + e.getErrorCode() + " - " + e.getErrorMessage();
            }
            log.warn("WxPayJsapiServiceImpl.refund.微信支付服务异常: {}", errorMsg, e);
            return ConvertWxResultToDTO.createErrorRefundResult(outRefundNo, errorMsg);
        } catch (Exception e) {
            log.error("WxPayJsapiServiceImpl.refund.系统异常", e);
            return ConvertWxResultToDTO.createErrorRefundResult(outRefundNo, "系统异常: " + e.getMessage());
        }
    }

    @Override
    public RefundResultDTO getRefundByOutRefundNo(String outRefundNo) {
        try {
            QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
            request.setOutRefundNo(outRefundNo);

            Refund refund = refundService.queryByOutRefundNo(request);

            if (refund == null) {
                log.warn("WxPayJsapiServiceImpl.getRefundByOutRefundNo.退款单不存在:{}", outRefundNo);
                return ConvertWxResultToDTO.createNotFoundRefundResult(outRefundNo);
            }

            RefundResultDTO refundResultDTO = ConvertWxResultToDTO.convertToRefundResult(refund);

            log.info("WxPayJsapiServiceImpl.getRefundByOutRefundNo.退款结果:{}", refundResultDTO);

            return refundResultDTO;
        } catch (ServiceException e) {
            if (e.getHttpStatusCode() == 404) {
                log.warn("WxPayJsapiServiceImpl.getRefundByOutRefundNo.退款单不存在:{}", outRefundNo);
                return ConvertWxResultToDTO.createNotFoundRefundResult(outRefundNo);
            } else {
                log.error("WxPayJsapiServiceImpl.getRefundByOutRefundNo.查询退款异常", e);
                return ConvertWxResultToDTO.createErrorRefundResult(outRefundNo, "查询退款异常: " + e.getMessage());
            }
        } catch (Exception e) {
            log.error("WxPayJsapiServiceImpl.getRefundByOutRefundNo.系统异常", e);
            return ConvertWxResultToDTO.createErrorRefundResult(outRefundNo, "系统异常: " + e.getMessage());
        }
    }


    // 基础工具方法
    private String readRequestBody(HttpServletRequest request) {
        try (BufferedReader reader = request.getReader()) {
            return reader.lines().collect(Collectors.joining());
        } catch (IOException e) {
            log.error("读取请求体失败", e);
            return null;
        }
    }

    private void writeResponse(HttpServletResponse response, String code, String msg) {
        try {
            response.setContentType("application/json");
            response.getWriter().write(String.format("{\"code\":\"%s\",\"message\":\"%s\"}", code, msg));
            if ("FAIL".equals(code)) {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            } else {
                response.setStatus(HttpServletResponse.SC_OK);
            }
        } catch (IOException e) {
            log.error("响应写入失败", e);
        }
    }
}
